package com.demo.jvm.structure;

import com.demo.jvm.structure.constant.CONSTANT_info;
import com.demo.jvm.structure.constant.parser.CONSTANT_info_Parser;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;

public class ClazzReader {
    private String file;

    public ClazzReader(String file) {
        this.file = file;
    }


    /**
     * 读取class文件
     *
     * @return
     */
    public Clazz read() {
        Clazz clazz = new Clazz();
        try (DataInputStream dis = new DataInputStream(new FileInputStream(new File(file)))) {
            this.readMagic(dis, clazz);
            this.readVersion(dis, clazz);
            this.readConstantPool(dis, clazz);
            this.readAccessFlags(dis, clazz);
            this.readThisClass(dis, clazz);
            this.readSuperClass(dis, clazz);
            this.readInterface(dis, clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return clazz;
    }

    /**
     * 读取魔数
     *
     * @param dis
     * @param clazz
     * @throws Exception
     */
    private void readMagic(DataInputStream dis, Clazz clazz) throws Exception {
        int magic = dis.readInt();
        if (magic != 0xCAFEBABE) {
            throw new RuntimeException("当前文件不是class文件");
        }
        clazz.setMagic(magic);
    }

    /**
     * 读取版本号
     *
     * @param dis
     * @param clazz
     * @throws Exception
     */
    private void readVersion(DataInputStream dis, Clazz clazz) throws Exception {
        int minorVersion = dis.readUnsignedShort();
        int majorVersion = dis.readUnsignedShort();
        clazz.setMinorVersion(minorVersion);
        clazz.setMajorVersion(majorVersion);
    }

    /**
     * 读取常量池数据
     *
     * @param dis
     * @param clazz
     * @throws Exception
     */
    private void readConstantPool(DataInputStream dis, Clazz clazz) throws Exception {
        CONSTANT_info_Parser parser = new CONSTANT_info_Parser();

        int constantPoolCount = dis.readUnsignedShort();
        clazz.setConstantPoolCount(constantPoolCount);

        CONSTANT_info[] cpInfos = new CONSTANT_info[constantPoolCount - 1];
        for (int i = 1; i <= constantPoolCount - 1; i++) {
            int tag = dis.readUnsignedByte();
            CONSTANT_info info = parser.parse(tag, dis, i - 1);
            cpInfos[i - 1] = info;
        }
        clazz.setCpInfos(cpInfos);
    }

    private void readAccessFlags(DataInputStream dis, Clazz clazz) throws Exception {
        int accessFlags = dis.readUnsignedShort();
        List<ClassAccessFlagsType> types = ClassAccessFlagsType.getaccessFlagsTypeByValue(accessFlags);
        clazz.setClassAccessFlags(types);
    }

    /**
     * 读取thisClass
     *
     * @param dis
     * @param clazz
     * @throws Exception
     */
    private void readThisClass(DataInputStream dis, Clazz clazz) throws Exception {
        int thisClass = dis.readUnsignedShort();
        clazz.setThisClass(thisClass);
    }

    /**
     * 读取 super_class
     *
     * @param dis
     * @param clazz
     * @throws Exception
     */
    private void readSuperClass(DataInputStream dis, Clazz clazz) throws Exception {
        int superClass = dis.readUnsignedShort();
        clazz.setSuperClass(superClass);
    }

    /**
     * 读取 interface
     *
     * @param dis
     * @param clazz
     * @throws Exception
     */
    private void readInterface(DataInputStream dis, Clazz clazz) throws Exception {
        int interfacesCount = dis.readUnsignedShort();
        clazz.setInterfacesCount(interfacesCount);
        for (int i = 0; i < interfacesCount; i++) {
            int cpInfoIndex = dis.readUnsignedShort();
            clazz.getInterfaces().add(cpInfoIndex);
        }
    }
}
